/*
 * Copyright 2011 Clean GWT
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleanform.gwt.chinesechess.client.activity;

import java.util.ArrayList;
import java.util.List;

import com.cleanform.gwt.bootstrap.client.ui.Button;
import com.cleanform.gwt.bootstrap.client.ui.HeadingPanel;
import com.cleanform.gwt.bootstrap.client.ui.HeadingPanel.PanelType;
import com.cleanform.gwt.chinesechess.client.Chinesechess;
import com.cleanform.gwt.chinesechess.client.comet.CometClient;
import com.cleanform.gwt.chinesechess.client.comet.ServerPushEvent;
import com.cleanform.gwt.chinesechess.client.comet.ServerPushEvent.ServerPushEventHandler;
import com.cleanform.gwt.chinesechess.client.widgets.MessagePane;
import com.cleanform.gwt.chinesechess.shared.action.Actions;
import com.cleanform.gwt.chinesechess.shared.comet.UserEvent;
import com.cleanform.gwt.chinesechess.shared.comet.UserEvents;
import com.cleanform.gwt.chinesechess.shared.form.ChessForm;
import com.cleanform.gwt.chinesechess.shared.form.GameForm;
import com.cleanform.gwt.chinesechess.shared.model.Chess;
import com.cleanform.gwt.chinesechess.shared.model.ChessGame;
import com.cleanform.gwt.chinesechess.shared.model.ChessMove;
import com.cleanform.gwt.chinesechess.shared.model.ChessboardState;
import com.cleanform.gwt.chinesechess.shared.model.GameHelper;
import com.cleanform.gwt.json.client.ReflectDeserializer;
import com.cleanform.gwt.uibinder.client.RequiresActionRebind;
import com.cleanform.gwt.uibinder.client.UiHandler;
import com.cleanform.gwt.user.client.ActionEvent;
import com.cleanform.gwt.view.client.DefaultPendingHandler;
import com.cleanform.gwt.view.client.PendingHandler;
import com.cleanform.gwt.view.client.activity.AbstractActivity;
import com.cleanform.gwt.web.client.ObjectCallback;
import com.cleanform.gwt.web.client.Requests;
import com.cleanform.gwt.web.client.StatedCallback;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

import static com.cleanform.gwt.user.client.ui.Layouts.as;
import static com.cleanform.gwt.user.client.ui.Layouts.center;
import static com.cleanform.gwt.user.client.ui.Layouts.div;
import static com.cleanform.gwt.user.client.ui.Layouts.hor;
import static com.cleanform.gwt.user.client.ui.Layouts.ver;
import static com.cleanform.gwt.user.client.ui.TableLayouts.td;

/**
 * The raw widget for chess game
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public abstract class ChessGameView extends AbstractActivity implements RequiresActionRebind {
	static String getChessFace(int chess) {
		if (chess < 0) {
			return Chess.CHESS_FACES[0][-chess - 1];
		} else if (chess > 0) {
			return "<font color = 'red'>" + Chess.CHESS_FACES[1][chess - 1] + "</font>";
		}
		return null;
	}

	private HandlerRegistration closingRegistration;

	HeadingPanel chesstable = new HeadingPanel(PanelType.PRIMARY);

	Grid chessboard = new Grid(10, 9);
	Button btnExitGame = new Button("Exit");
	Button btnAdmitDefeat = new Button("Defeat");
	Button btnJoinGame = new Button("Join");
	Button btnPlayerRed = new Button("<font color='red'>帥</font>");
	Label txtPlayerRed = new Label();
	Button btnPlayerBlack = new Button("將");
	Label txtPlayerBlack = new Label();
	MessagePane messagePane = new MessagePane();
	CometClient cometClient = CometClient.getInstance();
	HandlerRegistration cometRegistration;
	ChessGame game;
	ChessboardState state = new ChessboardState();

	PendingHandler pendingHandler = new DefaultPendingHandler(btnPlayerBlack);
	/**
	 * The default view is for black-force, set true to reverse it.
	 */
	boolean reverseDeck;

	// user action
	int selectedY = -1;
	int selectedX = -1;
	List<ChessMove> moves;

	public ChessGameView() {
		chesstable.setContentWidget(center(chessboard));

		chesstable.getElement().getStyle().setMargin(0, Unit.PX);
		chessboard.setSize("420px", "480px");

		HeadingPanel options = new HeadingPanel(PanelType.PRIMARY);
		options.setCaption("Options");
		options.setContentWidget(div(as(ver(btnExitGame, btnAdmitDefeat, btnJoinGame)).right()));
		options.getElement().getStyle().setMargin(0, Unit.PX);

		Widget pnlInfo = ver(
				td(as(options).height(140)).top(),
				hor(btnPlayerRed, txtPlayerRed),
				hor(btnPlayerBlack, txtPlayerBlack),
				td(messagePane).bottom());

		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 9; j++) {
				chessboard.getCellFormatter().setWidth(i, j, "45px");
				chessboard.getCellFormatter().setHeight(i, j, "45px");
			}
		}

		initWidget(hor(td(chesstable).width("100%"), as(pnlInfo).width(260).height()));
		this.setSize("100%", "560px");
		this.addEventHandlers();

		state.reset();
		redrawChessboard();
	}

	public void applyMove(ChessMove move) {
		moveChess(move.getSx(), move.getSy(), move.getTx(), move.getTy());
		state.applyMove(move);
		GameHelper.applyMove(game,move);
		if (move.getTarget() == Chess.KING) {
			messagePane.appendSysMessage(game.getPlayerBlack() + " Wins!");
			resetGame();
		} else if (move.getTarget() == -Chess.KING) {
			messagePane.appendSysMessage(game.getPlayerRed() + " Wins!");
			resetGame();
		} else {
			updateGameState();
		}

	}

	void defaultDeckPosition() {
		reverseDeck = Chinesechess.getUser().equals(game.getPlayerRed());
	}

	protected void onUnload() {
		exit();
	}

	public void enter(ChessGame value) {
		setGame(value);
		if (value.getChessMoves() != null) {
			for (ChessMove move : value.getChessMoves()) {
				moveChess(move.getSx(), move.getSy(), move.getTx(), move.getTy());
				state.applyMove(move);
			}
		}
	}

	public void exit() {
		//SimpleObject form = getGameForm();
		GameForm form = getGameForm();
		String url = Requests.buildUrl(Actions.GAME_CONTROLLER, Actions.EXIT_GAME);
		Requests.postObject(url, form, new ObjectCallback<String>(String.class) {

			@Override
			protected void onCallback(String response) {
				getManager().setActive(0);
			}
		});
	}
	
	public ChessForm createChessForm() {
		ChessForm gameForm = GWT.create(ChessForm.class);
		gameForm.setActor(Chinesechess.getUser());
		if(game != null){
			gameForm.setGameid(game.getGameid());
		}
		return gameForm;
	}

	public GameForm getGameForm() {
		GameForm gameForm = GWT.create(GameForm.class);
		gameForm.setActor(Chinesechess.getUser());
		if(game != null){
			gameForm.setGameid(game.getGameid());
		}
		//SimpleObject gameForm = new SimpleObject();
//		gameForm.put("actor", Chinesechess.getUser());
//		if (game != null) {
//			gameForm.put("gameid", game.getGameid());
//		}
		return gameForm;
	}

	public int getUserActive() {
		String clientid = (String) Chinesechess.getUser();
		if (clientid.equals(game.getPlayerRed())) {
			return ChessGame.ACTIVE_RED;
		} else if (clientid.equals(game.getPlayerBlack())) {
			return ChessGame.ACTIVE_BLACK;
		} else {
			return 0;
		}
	}

	public boolean hasSelection() {
		return selectedY >= 0 && selectedX >= 0;
	}

	void moveChess(int sx, int sy, int tx, int ty) {
		if (reverseDeck) {
			sx = ChessboardState.HBound - sx;
			sy = ChessboardState.VBound - sy;
			tx = ChessboardState.HBound - tx;
			ty = ChessboardState.VBound - ty;
		}
		chessboard.setWidget(ty, tx, chessboard.getWidget(sy, sx));
	}

	@Override
	public void onActive(int relatedIndex) {
		if (closingRegistration == null) {
			closingRegistration = Window.addWindowClosingHandler(new ClosingHandler() {

				@Override
				public void onWindowClosing(ClosingEvent event) {
					exit();
				}
			});

			cometRegistration = cometClient.addServerPushHandler(new ServerPushEventHandler() {

				@Override
				public void onServerPush(ServerPushEvent event) {
					receiveEvents(event.getComet());
				}
			});
			cometClient.startPolling((String) Chinesechess.getUser());
		}
	}

	public void onInactive() {

		if (closingRegistration != null) {
			setGame(null);
			messagePane.reset();
			cometClient.stopPolling();

			closingRegistration.removeHandler();
			cometRegistration.removeHandler();
			closingRegistration = null;
			cometRegistration = null;
		}
	}

	/**
	 * Processes on comet events callback.
	 * 
	 * @param events the comet events
	 */
	void receiveEvents(List<UserEvent> events) {
		if (events != null) {
			for (UserEvent e : events) {
				String type = e.getType();
				if (UserEvents.JOIN_GAME.equals(type)) {
					GameHelper.playerJoin(game, e.getActor());
					messagePane.appendSysMessage("Welcome " + e.getActor() + " to join this game");
					setGame(game);
				} else if (UserEvents.OBSERVE_GAME.equals(type)) {
					GameHelper.addObserver(game,e.getActor());
					messagePane.appendSysMessage("Welcome " + e.getActor() + " to join this game");
				} else if (UserEvents.EXIT_GAME.equals(type)) {
					GameHelper.playerExit(game, e.getActor());
					messagePane.appendSysMessage(e.getActor() + " exit game");
					if (!GameHelper.hasGamePlayer(game)) {
						// ForwardEvent.fire(this, "CwGameHall");
						// manager.setActive(0);
						getManager().setActive(0);
					} else if (!GameHelper.checkGameReady(game)) {
						GameHelper.restart(game);
						setGame(game);
					}
				} else if (UserEvents.MOVE_CHESS.equals(type)) {
					applyMove(e.getMove());
				} else if (UserEvents.ADMIT_DEFEAT.equals(type)) {
					resetGame();
					messagePane.appendSysMessage(e.getActor() + " admit defeat");
				} else if (UserEvents.GAME_MSG.equals(type)) {
					messagePane.appendMessage(e.getActor(), e.getMessage());
				}
			}
		}

	}

	public void redrawChessboard() {
		chessboard.clear();
		final int[][] pieces = state.getPieces();
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 9; j++) {
				if (pieces[i][j] != 0) {
					final Button chess = new Button(getChessFace(pieces[i][j]));
					if (reverseDeck) {
						chessboard.setWidget(ChessboardState.VBound - i, ChessboardState.HBound - j, chess);
					} else {
						chessboard.setWidget(i, j, chess);
					}
				}
			}
		}
	}

	public void resetGame() {
		GameHelper.restart(game);
		state.reset();
		updateGameState();
		redrawChessboard();
	}

	public void resetSelection() {
		selectedX = -1;
		selectedY = -1;
		moves = null;
	}

	public void selectedChess(int x, int y) {
		selectedX = x;
		selectedY = y;
		moves = new ArrayList<ChessMove>();
		for (ChessMove[][] pMoves : state.getAvalibleMoves3(game.getActive())) {
			for (ChessMove[] tMoves : pMoves) {
				for (ChessMove move : tMoves) {
					if (move == null) {
						break;
					}
					moves.add(move);
				}
			}
		}
	}

	public void setGame(ChessGame game) {
		this.game = game;
		if (game != null) {
			boolean isPlayer = GameHelper.checkGamePlayer(game,Chinesechess.getUser());
			defaultDeckPosition();
			chesstable.setCaption(game.getDesc());
			txtPlayerRed.setText(game.getPlayerRed());
			txtPlayerBlack.setText(game.getPlayerBlack());
			btnAdmitDefeat.setVisible(isPlayer);
			btnJoinGame.setVisible(!isPlayer);
		} else {
			chesstable.setCaption("");
			txtPlayerRed.setText("");
			txtPlayerBlack.setText("");
		}
		state.reset();
		updateGameState();
		redrawChessboard();
	}

	private void updateGameState() {
		if (game != null) {
			btnAdmitDefeat.setEnabled(GameHelper.checkGameStart(game));
			btnJoinGame.setEnabled(!GameHelper.checkGameReady(game));
		}
	}

	@UiHandler("chessboard")
	void onChessboardClicked(ClickEvent e) {
		Cell cell = chessboard.getCellForEvent(e);
		if (cell == null || game.getActive() != getUserActive()) {
			return;
		}
		int y = cell.getRowIndex();
		int x = cell.getCellIndex();
		if (reverseDeck) {
			x = ChessboardState.HBound - x;
			y = ChessboardState.VBound - y;
		}
		if (state.getForce(x, y) == game.getActive()) {
			selectedChess(x, y);
		} else {
			if (GameHelper.checkGameReady(game) && hasSelection()) {
				final ChessMove move = state.getChessMove(selectedX, selectedY, x, y);
				if (moves.contains(move)) {
					resetSelection();
					applyMove(move);

					//SimpleObject form = getGameForm();
//					form.put("sx", move.getSx());
//					form.put("sy", move.getSy());
//					form.put("tx", move.getTx());
//					form.put("ty", move.getTy());
//					form.put("source", move.getSource());
//					form.put("target", move.getTarget());
					ChessForm form = createChessForm();
					form.setSx(move.getSx());
					form.setSy(move.getSy());
					form.setTx(move.getTx());
					form.setTy(move.getTy());
					form.setSource(move.getSource());
					form.setTarget(move.getTarget());
					Requests.postObject("gameController/moveChess.do", form, new ObjectCallback<String>(String.class,game
							.getPlayerBlack().startsWith("[AI]") ? pendingHandler : DefaultPendingHandler.BLOCKED) {

						@Override
						protected void onCallback(String response) {
						}
					});
				}
			}
		}
	}

	@UiHandler("btnAdmitDefeat")
	void onBtnAdmitDefeatClick(ClickEvent e) {
		String url = Requests.buildUrl(Actions.GAME_CONTROLLER, Actions.ADMIT_DEFEAT);
		Requests.postObject(url, getGameForm(), new ObjectCallback<String>(String.class) {

			@Override
			protected void onCallback(String response) {
				resetGame();
			}
		});
	}

	@UiHandler("btnExitGame")
	void onBtnExitGameClick(ClickEvent e) {
		exit();
	}

	@UiHandler("btnJoinGame")
	void onBtnJoinGameClick(ClickEvent e) {
		GameForm gameForm = getGameForm();
		if (!GameHelper.checkGameReady(game)) {
			String url = Requests.buildUrl(Actions.GAME_CONTROLLER, Actions.JOIN_GAME);
			Requests.postObject(url, gameForm, new StatedCallback() {

				@Override
				protected void onSuccess(String result) {
					ChessGame game = ReflectDeserializer.get(ChessGame.class).deserialize(result);
					setGame(game);					
				}
			});
		}
	}

	@UiHandler({ "btnPlayerBlack", "btnPlayerRed" })
	void onChessboardClick(ClickEvent e) {
		if (e.getSource() == btnPlayerBlack) {
			if (reverseDeck) {
				reverseDeck = false;
				redrawChessboard();
			}
		} else if (e.getSource() == btnPlayerRed) {
			if (!reverseDeck) {
				reverseDeck = true;
				redrawChessboard();
			}
		}
	}

	@UiHandler("messagePane")
	void onMessagePaneAction(ActionEvent event) {
		final String msg = messagePane.getInputMessage();
		if (msg.isEmpty()) {
			return;
		}
		GameForm form = getGameForm();
		form.setMessage(msg);
		String url = Requests.buildUrl(Actions.GAME_CONTROLLER, Actions.GAME_MSG);
		Requests.postObject(url, form, new ObjectCallback<String>(null) {

			@Override
			protected void onCallback(String response) {
				messagePane.appendMessage((String) Chinesechess.getUser(), msg);
			}
		});
	}
}
